/* << fastinsert report library >> OciBase.cpp  Nov 1, 2010
 *
 * 	http://code.google.com/p/libfir
 *
 * Copyright (c) 2005 Pavlo Drobov (drobov.pavel@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */


/*
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 * ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA ALFA
 */






//http://download-uk.oracle.com/docs/cd/B14117_01/appdev.101/b10779/oci12obn.htm
//http://oracledocs.shu.ac.uk/oracle/B28359_01/appdev.111/b28395/oci13obn.htm

//example  OCIDefineArrayOfStruct
//http://forums.oracle.com/forums/thread.jspa?messageID=3147319

//http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28395/oci16rel003.htm#i459315
//OCIDefineByPos()



#include <string.h>

#include "Debug.h"
#include "GridBase.h"

#include "OciBase.h"

namespace dasco
{
namespace report
{

OciException::OciException(const char* txt,errorNumber n)
	:Text(txt)
{
	number=n;
}

OciConnect::OciConnect(const char* _login,const char* _passwd,const char* _db, const char * __charset)
	:charset(__charset)
{
	::strncpy(login,_login,sizeof(login));
	::strncpy(passwd,_passwd,sizeof(passwd));
	::strncpy(db,_db,sizeof(db));
}

const char* OciConnect::getLogin() const
{
	return login;
}
const char* OciConnect::getPassword() const
{
	return passwd;
}
const char* OciConnect::getDB() const
{
	return db;
}


/////////////////////////////////////////////////////////////////////
static void checkerr0(void* handle, ub4 htype,sword status) throw (OciException);
#define checkerr(errhp,status) checkerr0((errhp),OCI_HTYPE_ERROR,(status))
#define checkenv(envhp,status) checkerr0((envhp),OCI_HTYPE_ENV,(status))


///////// OCI Env variable ///
static OCIEnv * envhp;
static OCIError *errhp;
static OCIAuthInfo * authp;
static OCISvcCtx *svchp;

OciBase::OciBase(const OciConnect& conn)
{
	connect(&conn);
}


void OciBase::renderGrid(const IGrid*)
{
	//IT'S NOT IMPLEMENTED YET
}

void OciBase::save(const char*) throw (OciException)
{
	//IT'S NOT IMPLEMENTED YET
}

void OciBase::load(const char* sql, IGrid*** grids,unsigned* qnt) throw (OciException)
{
	*qnt =1;
	*grids = new  IGrid*[1];
	(*grids)[0] = callSQL(sql);
}

void OciBase::load(const char* const * sqlS,IGrid*** grids,unsigned* qnt) throw (OciException)
{
	*qnt = 0;
	for(const char * const *rabbit = sqlS; *rabbit; rabbit++, (*qnt)++);
	//DEBUG_MSG("We have " << *qnt << " sqls");

	*grids = new  IGrid*[*qnt];
	for(unsigned i=0; i< *qnt; ++i)
		(*grids)[i] = callSQL(sqlS[i]);

}


IGrid* OciBase::callSQL(const char* sql)  throw (OciException)
{
	OCIStmt 	 *stmthp;
	OCIParam     *paramhp;
	ub4          colsqnt = 0;

	MyOciDefine   *defhp, *defhpRabbit;

//    ub2   alenname = sizeof(ub4);        // return lengths
//	ub2   rcodename =0;          		// return codes


	IGrid* iGrid = new GridBase(sql);

	DEBUG_MSG("STMT for SQL: " << sql);
	checkerr(errhp
			,::OCIStmtPrepare2(svchp
							,&stmthp
							,errhp
							,(const OraText*)sql,::strlen(sql)
							,NULL, 0 //tagging parameters: optional
							,OCI_NTV_SYNTAX
							,OCI_DEFAULT
							)
			);

	checkerr(errhp
			,::OCIStmtExecute(svchp
									,stmthp
									,errhp
									//,(ub4) ARRAY_SIZE //feth rows quantity
									,0 //feth rows quantity
									,(ub4) 0 //offset rows
									,(CONST OCISnapshot *) NULL
									,(OCISnapshot *) NULL
									,OCI_DEFAULT
									//,OCI_STMT_SCROLLABLE_READONLY
									//,OCI_DESCRIBE_ONLY
									));

	checkerr(errhp
			,::OCIAttrGet(stmthp
					,OCI_HTYPE_STMT
					,&colsqnt,NULL
					,OCI_ATTR_PARAM_COUNT
					,errhp));

	//DEBUG_MSG("parmcnt: "  << colsqnt);

	defhpRabbit = defhp = new MyOciDefine[colsqnt];

	colsqnt = 0;
	IRowSetHeader * rowSetHeader = new RowSetHeader;
	while (OCI_SUCCESS == ::OCIParamGet(
										stmthp
										,OCI_HTYPE_STMT
										,errhp
										,(void**)&paramhp
										,++colsqnt))
	{
	   defhpRabbit->init(colsqnt,paramhp);
	   rowSetHeader->addColumn(*defhpRabbit); //add column name
	   defhpRabbit++;
	}
	iGrid->addRow(rowSetHeader);

	colsqnt--;

	//DEBUG_MSG("COUNTER COL number: " << colsqnt);


	//DEBUG_MSG("=====================FETCH ROWS=====================");

	//TODO: very dirty code!!! mast be rewrite
	std::list<AOciCell*> list;
	std::list<AOciCell*>::iterator midge, race_over;
	sword  status = OCI_SUCCESS;
	while(OCI_SUCCESS  == status)
	{
		for(defhpRabbit = defhp; (ub4)(defhpRabbit -defhp) < colsqnt; defhpRabbit++)
		{
			AOciCell * aa  = defhpRabbit->defineByPos(stmthp);
			list.push_back(aa);
		}

		status = ::OCIStmtFetch2(stmthp
								 ,errhp
								 ,1
								 ,OCI_FETCH_NEXT
								 ,0
								 ,OCI_DEFAULT);

		IRowSet * rowSet = new RowSet;
		midge = list.begin(); race_over = list.end();
		while(midge != race_over)
		{
			if(OCI_SUCCESS  == status)
			{
				//DEBUG_MSG("col: " << (*midge)->asText());
				rowSet->addColumn((*midge)->asText()); //add column
			}
			delete *midge;
			midge++;
		}
		list.clear();
		iGrid->addRow(rowSet);
	}

	if(OCI_NO_DATA !=  status)
		checkenv(errhp,status);

	delete [] defhp;


	checkerr(errhp
			,::OCIStmtRelease(stmthp,errhp,NULL,0,OCI_DEFAULT));

	return iGrid;
}



void OciBase::connect(const IConnect* iconn) throw (OciException)
{
	const OciConnect * conn = dynamic_cast<const OciConnect*> (iconn);
	if (!conn)
	{
		DEBUG_MSG("not OCI connect type");
		exit(-2);
	}

	ub2 charset; //= ::getCharSetIdFromName(conn->charset);

	// INITIALIZING OCI
	checkenv(envhp
			,conn->charset ?
			::OCIEnvNlsCreate(&envhp
							, OCI_DEFAULT
							, NULL, NULL,NULL, NULL //callbacks context
							, (size_t) 0 //extra memory size
							, (void**) NULL //returned extra memory
							, charset //clent side char set
							, charset //client side NCHAR char set
							)
			:
			::OCIEnvCreate(&envhp
					, OCI_DEFAULT
					, NULL, NULL, NULL, NULL //callbacks context
					, (size_t) 0 //extra memory size
					, (void**) NULL //returned extra memory
					)
			);

	checkenv(envhp
			,::OCIHandleAlloc(envhp
						 ,(void**)&errhp
						 ,OCI_HTYPE_ERROR
						 ,(size_t)0
						 ,(void**)NULL
						 ));

	// MANAGING CONNECTIONS
	//allocate auth handle
	checkenv(envhp
			,::OCIHandleAlloc(envhp
						,(void**)&authp
						,OCI_HTYPE_AUTHINFO
						,(size_t) 0
						,(void**) NULL
						));

	checkerr(errhp
			,::OCIAttrSet(authp
					 ,OCI_HTYPE_AUTHINFO
					 ,(void*)conn->getLogin(), ::strlen(conn->getLogin())
					 ,OCI_ATTR_USERNAME
					 ,errhp
					 ));

	checkerr(errhp
			,::OCIAttrSet(authp
					 ,OCI_HTYPE_AUTHINFO
					 ,(void*)conn->getPassword(), ::strlen(conn->getPassword())
					 ,OCI_ATTR_PASSWORD
					 ,errhp
					 ));

	DEBUG_MSG("try connect: " << conn->getDB());

	checkerr(errhp
			,::OCISessionGet(envhp
							,errhp
							,&svchp
							,authp
							,(OraText*)conn->getDB(),::strlen(conn->getDB())
							,NULL,0,NULL,NULL,NULL,OCI_DEFAULT
							));


	DEBUG_MSG("connect! ");
}

OciBase::~OciBase()
{
	::OCIHandleFree(authp,OCI_HTYPE_AUTHINFO);
	::OCIHandleFree(errhp,OCI_HTYPE_ERROR);
	::OCIHandleFree(envhp,OCI_HTYPE_ENV);
	DEBUG_MSG("Free OCI");
}

static void checkerr0(void* handle, ub4 htype,sword status) throw (OciException)
{
//#define  DEBUG_MSG_OCI(info) DEBUG_MSG("OCI checkerr: " << info)
#define  DEBUG_MSG_OCI(info)

	text errbuf[0x2FF];
	sb4 errcode;
	switch (status)
	{
		case OCI_INVALID_HANDLE:
			DEBUG_MSG_OCI("OCI_INVALID_HANDLE");
			throw OciException("OCI_INVALID_HANDLE",0);
			break;
		case OCI_ERROR:
			DEBUG_MSG_OCI("OCI_ERROR");
			if(handle)
			{
				::memset(errbuf,0,sizeof(errbuf));
				::OCIErrorGet(handle,1,NULL,&errcode,errbuf,sizeof(errbuf),htype);
				DEBUG_MSG_OCI("\terr msg: " <<  (char*)errbuf);
				throw OciException((const char*)errbuf,errcode);
			}
			else
			{
				DEBUG_MSG_OCI("unable to extract detailed diagnostic info");
				throw OciException("unable to extract detailed diagnostic info",0);
			}
			break;
		case OCI_SUCCESS_WITH_INFO:
			DEBUG_MSG_OCI("OCI_SUCCESS_WITH_INFO");
			if(handle)
			{
				::memset(errbuf,0,sizeof(errbuf));
				::OCIErrorGet(handle,1,NULL,&errcode,errbuf,sizeof(errbuf),htype);
				DEBUG_MSG_OCI("\t INFO msg: " <<  (char*)errbuf);
				throw OciException((const char*)errbuf,errcode);
			}

			break;
		case OCI_NEED_DATA:
			DEBUG_MSG_OCI("OCI_NEED_DATA");
			break;
		case OCI_NO_DATA:
			DEBUG_MSG_OCI("OCI_NO_DATA");
			break;
		case OCI_STILL_EXECUTING:
			DEBUG_MSG_OCI("OCI_STILL_EXECUTING");
			break;
		case OCI_CONTINUE:
			DEBUG_MSG_OCI("OCI_CONTINUE");
			break;
		default:
			break;
	}
}

void MyOciDefine::init(ub2 order,OCIParam* paramhp)
{
	col_order = order;

	// Retrieve the column name attribute
	checkerr(errhp
			,::OCIAttrGet(paramhp
						 ,OCI_DTYPE_PARAM
						 ,&col_name,&col_name_len
						 ,OCI_ATTR_NAME
						 ,errhp));

	DEBUG_MSG("COL NAME: " << (char*)col_name <<  "; col_name_len: " << col_name_len);

	// Retrieve the datatype attribute
	checkerr(errhp
			,::OCIAttrGet(paramhp
						 ,OCI_DTYPE_PARAM
						 ,&col_type,NULL
						 ,OCI_ATTR_DATA_TYPE
						 ,errhp));
	DEBUG_MSG("col_type: " << col_type );


	checkerr(errhp
			,::OCIAttrGet(paramhp
						,OCI_DTYPE_PARAM
						,&col_precesion,NULL
						,OCI_ATTR_PRECISION
						,errhp));
	DEBUG_MSG("col_precesion: " << col_precesion );

	checkerr(errhp
			,::OCIAttrGet(paramhp
						 ,OCI_DTYPE_PARAM
						 ,&col_scale,NULL
						 ,OCI_ATTR_SCALE
						 ,errhp));
	 DEBUG_MSG("col_scale: " << col_scale );


	 // Retrieve the length semantics for the column
	 checkerr(errhp
			 ,::OCIAttrGet(paramhp
						   ,OCI_DTYPE_PARAM
						   ,&char_semantics,NULL
						   ,OCI_ATTR_CHAR_USED
						   ,errhp));
	 DEBUG_MSG("char_semantics: "  << char_semantics);
	checkerr(errhp
			,::OCIAttrGet(paramhp
						 ,OCI_DTYPE_PARAM
						 ,&col_width,NULL
						 ,(char_semantics ? OCI_ATTR_CHAR_SIZE : OCI_ATTR_DATA_SIZE)
						 ,errhp ));

	 DEBUG_MSG("col_width: "  << col_width);
}

AOciCell * MyOciDefine::ociCellFactory() const
{
	AOciCell * ociCell;
	switch (col_type)
	{
	case OCI_TYPECODE_INTEGER:
	case OCI_TYPECODE_NUMBER:
		ociCell = new OciCell<OCI_TYPECODE_INTEGER>;
		break;
	default:
		ociCell = new OciCell<OCI_TYPECODE_VARCHAR2>(col_width) ;
		break;
	}
	ociCell->allocate();
	return ociCell;
}


AOciCell *  MyOciDefine::defineByPos(OCIStmt* stmthp)
{
	AOciCell * ociCell = ociCellFactory();
	checkerr(errhp
			,::OCIDefineByPos(stmthp
							 ,&define
							 ,errhp
							 ,col_order
							 ,&ociCell->iov, ociCell->iov.bfl
							 ,ociCell->_oci_typecode
							 ,&ociCell->indname
							 ,&alenname
							 ,&rcodename
							 ,OCI_IOV));

	return ociCell;
}


}}//end namespace dasco::report
